BINARYMAGIC
BINARYMAGIC
BINARYMAGIC
#include "stdafx.h"
#include "LoadData.h"
#include "bitmapData.h"
#include "HeapAllocator.h"
#include "Utils.h"
#include "includeIpp.h"

using namespace FTLib;


/*static*/const char CDataBase::BINARYMAGIC[12] = { 'F', 'F', 'T', 'I', 'E', 'R', 'B', 'I', 'N', 'A', 'R', 'Y' };// "FFTIERBINARY";

//******************************************************************************

CDataLoad::CDataLoad(const wchar_t* fileName, LoadDataFormat fmt /*= LoadDataFormat::Invalid*/)
	: fileName(fileName)
{
	if (fmt == LoadDataFormat::Invalid)
	{
		this->dataFormat = CDataLoad::DetermineLoadDataFormatFromExtension(this->fileName);
	}
	else
	{
		this->dataFormat = fmt;
	}
}

/*static*/LoadDataFormat CDataLoad::DetermineLoadDataFormatFromExtension(const std::wstring& filename)
{
	std::wstring::size_type s = filename.find_last_of(L".");
	std::wstring extension;
	if (s != std::wstring::npos)
	{
		extension = filename.substr(s + 1);
	}

	if (_wcsicmp(extension.c_str(), L"csv") == 0 || _wcsicmp(extension.c_str(), L"txt") == 0)
	{
		return LoadDataFormat::CSV;
	}
	else if (_wcsicmp(extension.c_str(), L"bin") == 0)
	{
		return LoadDataFormat::BINARY;
	}
	else if (_wcsicmp(extension.c_str(), L"wdp") == 0 || _wcsicmp(extension.c_str(), L"png") == 0)
	{
		return LoadDataFormat::WIC;
	}

	return LoadDataFormat::Invalid;
}

std::shared_ptr<IBitmapData> CDataLoad::ReadAsFloat32(std::uint32_t extraRows/* = 0*/, std::uint32_t extraColumns/* = 0*/)
{
	static const struct LoaderTabData {
		LoadDataFormat dataFormat;
		std::shared_ptr<IBitmapData>(CDataLoad::* mfp)(const wchar_t* fileName, std::function<std::shared_ptr<IBitmapData>(PixelType pixelType, int width, int height)> func);
	} LoaderTable[] =
	{
		{ LoadDataFormat::CSV, &CDataLoad::ReadCSV },
		{ LoadDataFormat::WIC, &CDataLoad::ReadWithWIC },
		{ LoadDataFormat::BINARY, &CDataLoad::ReadBINARY }
	};

	const LoaderTabData* ptrLoaderData = nullptr;
	for (int i = 0; i < ARRAYSIZE(LoaderTable); ++i)
	{
		if (LoaderTable[i].dataFormat == this->dataFormat)
		{
			ptrLoaderData = LoaderTable + i;
			break;
		}
	}

	if (ptrLoaderData == nullptr)
	{
		throw loadsavedata_exception(L"Invalid data-format.");
	}

	auto bm = (this->*(ptrLoaderData->mfp))(
		this->fileName.c_str(),
		[=](PixelType pixelType, int width, int height)
	{
		return CBitmapData<CHeapAllocator>::Create(pixelType/*PixelType::GrayFloat32*/, width, height, 0, extraRows, extraColumns);
	});

	return bm;
}

std::shared_ptr<IBitmapData> CDataLoad::ReadBINARY(const wchar_t* fileName, std::function<std::shared_ptr<IBitmapData>(PixelType pixelType, int width, int height)> func)
{
	std::ifstream inputFile;
	inputFile.open(fileName, std::ios::in | std::ios::binary);

	// check magic
	char magic[sizeof(CDataBase::BINARYMAGIC)];
	inputFile.read(magic, sizeof(CDataBase::BINARYMAGIC));
	if (memcmp(magic, CDataBase::BINARYMAGIC, sizeof(CDataBase::BINARYMAGIC)) != 0)
	{
		throw loadsavedata_exception(L"Invalid file");
	}

	// read information
	uint32_t width, height,pixelFmtBin, dataLayoutBin;
	inputFile.read((char*)&width,sizeof(width));
	inputFile.read((char*)&height,sizeof(height));
	inputFile.read((char*)&pixelFmtBin, sizeof(pixelFmtBin));
	inputFile.read((char*)&dataLayoutBin, sizeof(dataLayoutBin));
	PixelType pixelType = (PixelType)pixelFmtBin;
	auto bd = func(pixelType, width, height);
	auto bitmapData = bd->Lock();
	size_t lineSize = Utils::GetBytesPerPel(pixelType)*width;
	for (int y = 0; y < height; ++y)
	{
		float* ptr = (float*)(((char*)bitmapData.ptrDataRoi) + y*bitmapData.pitch);

		inputFile.read((char*)ptr, lineSize);
	}

	bd->Unlock();
	inputFile.close();
	return bd;
}

std::shared_ptr<IBitmapData> CDataLoad::ReadCSV(const wchar_t* fileName, std::function<std::shared_ptr<IBitmapData>(PixelType pixelType, int width, int height)> func)
{
	std::ifstream inputFile;
	inputFile.open(fileName, std::ios::in);
	if (inputFile.fail())
	{
		std::wostringstream out;
		out << L"Couldn't open the file \"" << fileName << L"\"" << std::endl;;
		throw loadsavedata_exception(out.str());
	}

	std::string line;
	std::getline(inputFile, line);
	if (inputFile.fail())
	{
		CDataLoad::ThrowErrorReadingCsv(fileName);
	}

	CSVBitmapInfo bitmapInfo;
	bool b = ParseInfoLine(line, bitmapInfo);
	if (b == false)
	{
		inputFile.seekg(0, std::ios::beg);
		this->GetDimensionByParsingCSVFile(inputFile, bitmapInfo);
		inputFile.seekg(0, std::ios::beg);
	}

	auto bd = func(bitmapInfo.pixelType, bitmapInfo.width, bitmapInfo.height);
	auto bitmapData = bd->Lock();

	int numbersPerPixel = 1;
	if (bitmapInfo.pixelType == PixelType::ComplexFloat32 || bitmapInfo.pixelType == PixelType::ComplexDouble64)
	{
		numbersPerPixel = 2;
	}

	// parse the first line
	for (int y = 0; y < bitmapInfo.height; ++y)
	{
		float* ptr = (float*)(((char*)bitmapData.ptrDataRoi) + y*bitmapData.pitch);
		std::getline(inputFile, line);
		if (inputFile.fail())
		{
			CDataLoad::ThrowErrorReadingCsv(fileName);
		}

		ParseLineFloat(line, ptr, numbersPerPixel*bd->GetWidth());
	}

	bd->Unlock();
	return bd;
}

/*static*/void CDataLoad::ThrowErrorReadingCsv(const wchar_t* fileName)
{
	std::wostringstream out;
	out << L"Error with reading the CSV-file \"" << fileName << L"\"" << std::endl;;
	throw loadsavedata_exception(out.str());
}

/*static*/void CDataLoad::GetDimensionByParsingCSVFile(std::ifstream& inputFile, CSVBitmapInfo& bitmapInfo)
{
	int width = -1;
	int height = 0;
	std::string line;
	for (;;)
	{
		std::getline(inputFile, line);
		if (line.empty() || std::all_of(line.begin(), line.end(), isspace))
		{
			continue;
		}

		int w = CountNumbersInLine(line);
		width = (std::max)(width, w);
		++height;
		if (inputFile.eof())
		{
			break;
		}
	}

	bitmapInfo.width = width;
	bitmapInfo.height = height;
	bitmapInfo.pixelType = PixelType::GrayFloat32;
}

/*static*/int CDataLoad::CountNumbersInLine(const std::string& line)
{
	size_t n = std::count(line.begin(), line.end(), ',');
	return ((int)n) + 1;
}

/*static*/bool CDataLoad::ParseLineFloat(const std::string& line, float* ptr, uint32_t width)
{
	const char* startPtr = line.c_str();
	char* endptr;
	for (uint32_t x = 0; x < width; ++x)
	{
		float f = strtof(startPtr, &endptr);
		if (f == 0 && startPtr == endptr)
		{
			throw loadsavedata_exception(L"Illegal number encountered");
		}

		if ((f == HUGE_VAL || f == -HUGE_VAL) && errno != 0)
		{
			throw loadsavedata_exception(L"Illegal number encountered");
		}

		ptr[x] = f;

		startPtr = endptr;

		// now skip all spaces/tabs and also (exactly) one comma
		for (;; ++startPtr)
		{
			const char c = *startPtr;
			if (c == '\0')
			{
				break;
			}

			if (isspace(c))
			{
				continue;
			}

			if (c == ',')
			{
				startPtr++;
				break;
			}

			if (startPtr != endptr)
			{
				startPtr--;
			}

			break;
		}
	}

	return true;
}

/*static*/bool CDataLoad::ParseInfoLine(const std::string& infoLine, CSVBitmapInfo& bitmapInfo)
{
	// check if the line starts with a "#"
	for (char c : infoLine)
	{
		if (isspace(c))
		{
			continue;
		}

		if (c != '#')
		{
			return false;
		}

		break;
	}

	std::string::size_type s = infoLine.find_first_of("0123456789");
	if (s == std::string::npos)
	{
		throw loadsavedata_exception(L"Invalid info line");
	}

	std::string line = infoLine.substr(s);

	try
	{
		std::regex re("(\\d+)\\s+(\\d+)\\s+(FLOAT|DOUBLE|COMPLEXFLOAT|COMPLEXDOUBLE)", std::regex_constants::icase);
		auto it = std::sregex_iterator(line.begin(), line.end(), re);
		std::sregex_iterator end;
		if (it == end)
		{
			throw loadsavedata_exception(L"Invalid info line");
		}

		std::smatch match = *it;
		if (match.size() != 4)
		{
			throw loadsavedata_exception(L"Invalid info line");
		}

		std::string widthStr = match[1];
		std::string heightStr = match[2];
		std::string typeStr = match[3];

		bitmapInfo.width = std::stoul(widthStr);
		bitmapInfo.height = std::stoul(heightStr);
		if (strcmp(typeStr.c_str(), "FLOAT") == 0)
		{
			bitmapInfo.pixelType = PixelType::GrayFloat32;// ComplexFloat32;
		}
		else if (strcmp(typeStr.c_str(), "DOUBLE") == 0)
		{
			bitmapInfo.pixelType = PixelType::GrayDouble64;// ComplexDouble64;
		}
		else if (strcmp(typeStr.c_str(), "COMPLEXFLOAT") == 0)
		{
			bitmapInfo.pixelType = PixelType::ComplexFloat32;
		}
		else if (strcmp(typeStr.c_str(), "COMPLEXDOUBLE") == 0)
		{
			bitmapInfo.pixelType = PixelType::ComplexDouble64;
		}
		else
		{
			std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
			std::wostringstream out;
			out << L"Invalid info line - unknown type \"" << converter.from_bytes(typeStr.c_str()) << L"\"";
			throw loadsavedata_exception(out.str());
		}

		return true;
	}
	catch (std::regex_error& e)
	{
		//throw std::invalid_argument("Invalid file.");
		throw loadsavedata_exception(L"Invalid info line");
	}

	return false;
}

/// <summary>
/// Reads the with wic.
/// </summary>
/// <param name="fileName">Name of the file.</param>
/// <param name="func">The function.</param>
/// <returns></returns>
std::shared_ptr<IBitmapData> CDataLoad::ReadWithWIC(const wchar_t* fileName, std::function<std::shared_ptr<IBitmapData>(PixelType pixelType, int width, int height)> func)
{
	CComPtr<IWICImagingFactory> cpWicImagingFactory;
	HRESULT hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, (LPVOID*)&cpWicImagingFactory);
	ThrowIfFailed("Creating WICImageFactory", hr);

	CComPtr<IWICBitmapDecoder> cpDecoder;
	CComPtr<IWICBitmapFrameDecode> cpFrame;

	// Create a decoder for the given image file
	hr = cpWicImagingFactory->CreateDecoderFromFilename(fileName, NULL, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &cpDecoder);
	ThrowIfFailed("IWICImagingFactory::CreateDecoderFromFilename", hr);

	// Retrieve the first frame of the image from the decoder
	hr = cpDecoder->GetFrame(0, &cpFrame);
	ThrowIfFailed("IWICBitmapDecoder::GetFrame", hr);

	/*IWICColorContext* colorContexts[1];
	cpWicImagingFactory->CreateColorContext(&(colorContexts[0]));
	UINT actualCount;
	cpFrame->GetColorContexts(1, colorContexts, &actualCount);*/

	UINT nWidth, nHeight;
	// Retrieve the image dimensions
	hr = cpFrame->GetSize(&nWidth, &nHeight);
	ThrowIfFailed("IWICBitmapFrameDecode::GetSize", hr);

	// How WIC (IWICFormatConverter in particular) handles the conversion from an integer
	// format to 32bppGrayFloat is beyond me. I have no idea how the result is to be interpreted,
	// the only piece of information I could find is here: http://directxtex.codeplex.com/workitem/643
	// "Furthermore, Use of IWICFormatConverter implicitly assumes all integer content is sRGB and all fixed / float
	// content is scRGB so there's an implicit conversion of color space which is not desired."
	// Therefore we have to do the conversion by hand.

	WICRect rect = { 0, 0, nWidth, nHeight };
	CComPtr<IWICBitmapSource> spFormatConverted = Convert(cpWicImagingFactory, cpFrame);

	// Pixeltype of "spFormatConverted" is now guaranteed to be either GUID_WICPixelFormat8bppGray or GUID_WICPixelFormat16bppGray,
	// other types need to be added later.

	//hr = cpFrame->CopyPixels(&rect, nWidth, nHeight*nWidth, pbTest);

	auto bd = func(PixelType::GrayFloat32, nWidth, nHeight);

	CopyToFloatBitmap(spFormatConverted, bd);

	return bd;
	/*
	// Furthermore, Use of IWICFormatConverter implicitly assumes all integer content is sRGB and all fixed / float
	// content is scRGB so there's an implicit conversion of color space which is not desired.
	//
	// Convert the format of the image frame to float-grayscale is from
	CComPtr<IWICFormatConverter> cpConvertedFrame;
	hr = cpWicImagingFactory->CreateFormatConverter(&cpConvertedFrame);
	ThrowIfFailed("IWICImagingFactory::CreateFormatConverter", hr);

	hr = cpConvertedFrame->Initialize(
	cpFrame,							 // Source frame to convert
	GUID_WICPixelFormat32bppGrayFloat,   // The desired pixel format
	WICBitmapDitherTypeNone,		     // The desired dither pattern
	nullptr,							 // The desired palette
	0.f,								 // The desired alpha threshold
	WICBitmapPaletteTypeCustom);		 // Palette translation type
	ThrowIfFailed("IWICFormatConverter::CreateFormatConverter", hr);

	auto bd = func(nWidth, nHeight);
	//WICPixelFormatGUID pixelFormat;
	//hr = cpFrame->GetPixelFormat(&pixelFormat);
	//WICRect rect = { 0, 0, nWidth, nHeight };

	auto bitmapData = bd->Lock();
	hr = cpConvertedFrame->CopyPixels(&rect, bitmapData.pitch, bd->GetHeight()*bitmapData.pitch, (BYTE*)bitmapData.ptrDataRoi);
	bd->Unlock();

	// I have no idea how WIC exactly converts to a float image, in any case it does not
	// seem to normalize the image (so that the max is exactly 1). So we are doing this here
	// ourselves.
	Utils::NormalizeFloatImage(bd);

	return bd;*/
}

void CDataLoad::CopyToFloatBitmap(IWICBitmapSource* pSrc, std::shared_ptr<IBitmapData> dest)
{
	// precondition: pSrc is either Gray8 or Gray16, and dest is float
	UINT nWidth, nHeight;
	HRESULT hr = pSrc->GetSize(&nWidth, &nHeight);
	WICPixelFormatGUID pixelFormat;
	hr = pSrc->GetPixelFormat(&pixelFormat);
	UINT bytesPerPel;
	if (IsEqualGUID(pixelFormat, GUID_WICPixelFormat8bppGray))
	{
		bytesPerPel = 1;
	}
	else if (IsEqualGUID(pixelFormat, GUID_WICPixelFormat16bppGray))
	{
		bytesPerPel = 2;
	}
	else
	{
		throw loadsavedata_exception(L"Illegal pixeltype.");
	}

	UINT stride = ((nWidth*bytesPerPel + 7) / 8) * 8;

	struct free_delete
	{
		void operator()(void* x) { _aligned_free(x); }
	};
	std::unique_ptr<void, free_delete> buffer(_aligned_malloc(stride*nHeight, 8));

	WICRect rect = { 0, 0, nWidth, nHeight };
	hr = pSrc->CopyPixels(&rect, stride, stride*nHeight, (BYTE*)buffer.get());

	auto bitmapData = dest->Lock();
	IppiSize sizeDest = { dest->GetWidth(), dest->GetHeight() };
	float minValue, maxValue;
	if (IsEqualGUID(pixelFormat, GUID_WICPixelFormat8bppGray))
	{
		Ipp8u minValue8u, maxValue8u;
		IppStatus status = ippiMinMax_8u_C1R((const Ipp8u*)buffer.get(), stride, sizeDest, &minValue8u, &maxValue8u);
		ippiConvert_8u32f_C1R((const Ipp8u*)buffer.get(), stride, (Ipp32f*)bitmapData.ptrDataRoi, bitmapData.pitch, sizeDest);
		minValue = minValue8u;
		maxValue = maxValue8u;
	}
	else if (IsEqualGUID(pixelFormat, GUID_WICPixelFormat16bppGray))
	{
		Ipp16u minValue16u, maxValue16u;
		IppStatus status = ippiMinMax_16u_C1R((const Ipp16u*)buffer.get(), stride, sizeDest, &minValue16u, &maxValue16u);
		ippiConvert_8u32f_C1R((const Ipp8u*)buffer.get(), stride, (Ipp32f*)bitmapData.ptrDataRoi, bitmapData.pitch, sizeDest);
		minValue = minValue16u;
		maxValue = maxValue16u;
	}

	buffer.reset();

	dest->Unlock();

	Utils::NormalizeFloatImage(dest, minValue, maxValue);
}

CComPtr<IWICBitmapSource> CDataLoad::Convert(IWICImagingFactory* pImgFactory, CComPtr<IWICBitmapFrameDecode> src)
{
	WICPixelFormatGUID fmt;
	HRESULT hr = src->GetPixelFormat(&fmt);
	if (IsEqualGUID(fmt, GUID_WICPixelFormat8bppGray) ||
		IsEqualGUID(fmt, GUID_WICPixelFormat16bppGray))
	{
		return CComPtr<IWICBitmapSource>(src);
	}

	CComPtr<IWICFormatConverter> cpConvertedFrame;
	hr = pImgFactory->CreateFormatConverter(&cpConvertedFrame);
	ThrowIfFailed("IWICImagingFactory::CreateFormatConverter", hr);

	return CComPtr<IWICBitmapSource>(cpConvertedFrame);
}

/*static*/void CDataBase::ThrowIfFailed(const char* function, HRESULT hr)
{
	if (FAILED(hr))
	{
		char errorMsg[255];
		_snprintf_s(errorMsg, _TRUNCATE, "COM-ERROR hr=0x%08X (%s)", hr, function);
		throw std::runtime_error(errorMsg);
	}
}

/*static*/void CDataBase::ThrowIfFailed(const char* function, HRESULT hr, std::function<bool(HRESULT)> checkFunc)
{
	if (checkFunc(hr) != true)
	{
		char errorMsg[255];
		_snprintf_s(errorMsg, _TRUNCATE, "COM-ERROR hr=0x%08X (%s)", hr, function);
		throw std::runtime_error(errorMsg);
	}
}

//******************************************************************************

CSaveData::CSaveData(const wchar_t* fileName, SaveDataFormat dataFormat)
	: fileName(fileName), format(dataFormat)
{
}

void CSaveData::SaveAsFloat32(std::shared_ptr<IBitmapData> bitmap)
{
	switch (this->format)
	{
	case SaveDataFormat::WDP:
		this->SaveWithWIC(this->fileName.c_str(), GUID_ContainerFormatWmp, GUID_WICPixelFormat32bppGrayFloat, bitmap);
		break;
	case SaveDataFormat::CSV:
		this->SaveAsCSV(this->fileName.c_str(), bitmap);
		break;
	case SaveDataFormat::BINARY:
		this->SaveAsBINARY(this->fileName.c_str(), bitmap);
		break;
	default:
		break;
	}
}

void CSaveData::SaveAsGray16(std::shared_ptr<IBitmapData> bitmap)
{
	switch (this->format)
	{
	case SaveDataFormat::PNG:
		this->SaveWithWIC(this->fileName.c_str(), GUID_ContainerFormatPng, GUID_WICPixelFormat16bppGray, bitmap);
		break;
	default:
		break;
	}
}

void CSaveData::SaveAsGray8(std::shared_ptr<IBitmapData> bitmap)
{
	switch (this->format)
	{
	case SaveDataFormat::PNG:
		this->SaveWithWIC(this->fileName.c_str(), GUID_ContainerFormatPng, GUID_WICPixelFormat8bppGray, bitmap);
		break;
	default:
		break;
	}
}

void CSaveData::SaveWithWIC(const wchar_t* filename, const GUID encoder, const WICPixelFormatGUID& wicPixelFmt, std::shared_ptr<IBitmapData> bitmap)
{
	CComPtr<IWICImagingFactory> cpWicImagingFactor;
	HRESULT hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, (LPVOID*)&cpWicImagingFactor);
	ThrowIfFailed("Creating WICImageFactory", hr);

	CComPtr<IWICStream> stream;
	// Create a stream for the encoder
	hr = cpWicImagingFactor->CreateStream(&stream);
	ThrowIfFailed("IWICImagingFactory::CreateStream", hr);

	// Initialize the stream using the output file path
	hr = stream->InitializeFromFilename(filename, GENERIC_WRITE);
	ThrowIfFailed("IWICStream::InitializeFromFilename", hr);

	this->SaveWithWIC(cpWicImagingFactor, stream, encoder, wicPixelFmt, bitmap);

	hr = stream->Commit(STGC_DEFAULT);
	ThrowIfFailed("IWICStream::Commit", hr, [](HRESULT ec){return SUCCEEDED(ec) || ec == E_NOTIMPL; });
}

void CSaveData::SaveWithWIC(IWICImagingFactory* pFactory, IWICStream* destStream, const GUID encoder, const WICPixelFormatGUID& wicPixelFmt, std::shared_ptr<IBitmapData> spBitmap)
{
	// cf. http://msdn.microsoft.com/en-us/library/windows/desktop/ee719797(v=vs.85).aspx
	/*CComPtr<IWICImagingFactory> cpWicImagingFactor;
	HRESULT hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, (LPVOID*)&cpWicImagingFactor);
	ThrowIfFailed("Creating WICImageFactory", hr);*/

	CComPtr<IWICBitmapEncoder> wicBitmapEncoder;
	HRESULT hr = pFactory->CreateEncoder(
		/*GUID_ContainerFormatPng*//*GUID_ContainerFormatTiff*//*GUID_ContainerFormatWmp*/encoder,
		nullptr,    // No preferred codec vendor.
		&wicBitmapEncoder);
	ThrowIfFailed("Creating IWICImagingFactory::CreateEncoder", hr);

	// Create encoder to write to image file
	hr = wicBitmapEncoder->Initialize(destStream, WICBitmapEncoderNoCache);
	ThrowIfFailed("IWICBitmapEncoder::Initialize", hr);

	CComPtr<IWICBitmapFrameEncode> frameEncode;
	hr = wicBitmapEncoder->CreateNewFrame(&frameEncode, nullptr);
	ThrowIfFailed("IWICBitmapEncoder::CreateNewFrame", hr);

	hr = frameEncode->Initialize(nullptr);
	ThrowIfFailed("IWICBitmapFrameEncode::CreateNewFrame", hr);

	hr = frameEncode->SetSize(spBitmap->GetWidth(), spBitmap->GetHeight());
	ThrowIfFailed("IWICBitmapFrameEncode::SetSize", hr);

	WICPixelFormatGUID pixelFormat = wicPixelFmt;/*GUID_WICPixelFormat32bppGrayFloat;*/
	hr = frameEncode->SetPixelFormat(&pixelFormat);
	ThrowIfFailed("IWICBitmapFrameEncode::SetPixelFormat", hr);

	const char* pixelFormatActual = Utils::WICPixelFormatToInformalString(pixelFormat);

	auto bitmapData = spBitmap->Lock();
	hr = frameEncode->WritePixels(spBitmap->GetHeight(), bitmapData.pitch, spBitmap->GetHeight()* bitmapData.pitch, (BYTE*)bitmapData.ptrDataRoi);
	spBitmap->Unlock();
	ThrowIfFailed("IWICBitmapFrameEncode::WritePixels", hr);

	hr = frameEncode->Commit();
	ThrowIfFailed("IWICBitmapFrameEncode::Commit", hr);

	hr = wicBitmapEncoder->Commit();
	ThrowIfFailed("IWICBitmapEncoder::Commit", hr);
}

void CSaveData::SaveAsCSV(const wchar_t* filename, std::shared_ptr<IBitmapData> bitmap)
{
	// Format: # WIDTH HEIGHT TYPE
	std::ofstream outputFile;
	outputFile.open(filename, std::ios::out);

	std::string pixeltypeStr;
	std::string fouriertransformDataLayout;
	CSaveData::DeterminePixeltypeString(bitmap, pixeltypeStr);
	CSaveData::DetermineFourierTransformDataLayoutString(bitmap, fouriertransformDataLayout);

	outputFile << "# " << bitmap->GetWidth() << " " << bitmap->GetHeight() << " " << pixeltypeStr;
	if (!fouriertransformDataLayout.empty())
	{
		outputFile << " (" << fouriertransformDataLayout << ")" << std::endl;
	}
	else
	{
		outputFile << std::endl;
	}

	outputFile.flags(std::ios::scientific);
	auto bd = bitmap->Lock();
	PixelType pixelType = bitmap->GetPixelType();
	if (pixelType == PixelType::GrayFloat32)
	{
		outputFile.precision(std::numeric_limits<float>::max_digits10);
		for (int y = 0; y < bitmap->GetHeight(); ++y)
		{
			const float* ptr = (const float*)(((char*)bd.ptrDataRoi) + y*bd.pitch);
			for (int x = 0; x < bitmap->GetWidth(); ++x)
			{
				outputFile << ptr[x];
				if (x + 1 != bitmap->GetWidth())
				{
					outputFile << ",  ";
				}
			}

			outputFile << std::endl;
		}
	}
	else if (pixelType == PixelType::ComplexFloat32)
	{
		outputFile.precision(std::numeric_limits<float>::max_digits10);
		for (int y = 0; y < bitmap->GetHeight(); ++y)
		{
			const float* ptr = (const float*)(((char*)bd.ptrDataRoi) + y*bd.pitch);
			for (int x = 0; x < bitmap->GetWidth() * 2; ++x)
			{
				outputFile << ptr[x];
				if (x + 1 != 2 * bitmap->GetWidth())
				{
					outputFile << ", ";
				}
			}

			outputFile << std::endl;
		}
	}

	bitmap->Unlock();

	outputFile.close();
}

void CSaveData::SaveAsBINARY(const wchar_t* filename, std::shared_ptr<IBitmapData> bitmap)
{
	std::ofstream outputFile;
	outputFile.open(filename, std::ios::out | std::ios::binary);

	outputFile.write(CDataBase::BINARYMAGIC, sizeof(CDataBase::BINARYMAGIC));
	uint32_t d;
	d = bitmap->GetWidth();
	outputFile.write((const char*)&d,sizeof(uint32_t));//		write(&d, sizeof(d));
	d = bitmap->GetHeight();
	outputFile.write((const char*)&d, sizeof(uint32_t));// write(&d, sizeof(d));
	d = (uint32_t)bitmap->GetPixelType();
	//outputFile.write(&d, sizeof(d));
	outputFile.write((const char*)&d, sizeof(uint32_t));

	d = 0xffffffff;
	auto bitmap2 = std::static_pointer_cast<IBitmapData2>(bitmap);
	if (bitmap2)
	{
		uint16_t semantics;
		uint8_t fourierTransformedDataLayout;
		bool b = bitmap2->GetValueUInt8(DATASTOREKEY_FOURIERTRANSFORMED_LAYOUT, fourierTransformedDataLayout);
		if (b == true)
		{
			d = fourierTransformedDataLayout;
		}
	}

	outputFile.write((const char*)&d, sizeof(uint32_t));

	auto bd = bitmap->Lock();
	PixelType pixelType = bitmap->GetPixelType();
	if (pixelType == PixelType::GrayFloat32)
	{
		for (int y = 0; y < bitmap->GetHeight(); ++y)
		{
			const float* ptr = (const float*)(((char*)bd.ptrDataRoi) + y*bd.pitch);
			outputFile.write((const char*)ptr, bitmap->GetWidth() * sizeof(float));
		}
	}
	else if (pixelType == PixelType::ComplexFloat32)
	{
		for (int y = 0; y < bitmap->GetHeight(); ++y)
		{
			const float* ptr = (const float*)(((char*)bd.ptrDataRoi) + y*bd.pitch);
			outputFile.write((const char*)ptr, bitmap->GetWidth() * 2 * sizeof(float));
		}
	}

	bitmap->Unlock();
	outputFile.close();
}


/*static*/void CSaveData::DeterminePixeltypeString(std::shared_ptr<IBitmapData> bitmap, std::string& str)
{
	switch (bitmap->GetPixelType())
	{
	case PixelType::ComplexFloat32:
		str = "COMPLEXFLOAT";
		break;
	case  PixelType::ComplexDouble64:
		str = "COMPLEXDOUBLE";
		break;
	case PixelType::GrayFloat32:
		str = "FLOAT";
		break;
	default:
		break;
	}
}

/*static*/void CSaveData::DetermineFourierTransformDataLayoutString(std::shared_ptr<IBitmapData> bitmap, std::string& str)
{
	auto bitmap2 = std::static_pointer_cast<IBitmapData2>(bitmap);
	if (bitmap2)
	{
		uint16_t semantics;
		uint8_t fourierTransformedDataLayout;
		bool b = bitmap2->GetValueUInt8(DATASTOREKEY_FOURIERTRANSFORMED_LAYOUT, fourierTransformedDataLayout);
		if (fourierTransformedDataLayout == DS_FOURIERTRANSFORMED_LAYOUT_FULL_FFTWPACKED)
		{
			str = "FFTW_LAYOUT";
		}
		else if (fourierTransformedDataLayout == DS_FOURIERTRANSFORMED_LAYOUT_FULL_IPPPACKED)
		{
			str = "IPP_LAYOUT";
		}

		return;
	}

	str.erase();
}

//******************************************************************************

std::shared_ptr<IBitmapData> FTLib::CreateRandomFloat32Image(uint32_t width, uint32_t height, FTLib::PixelType pixelType, float low, float high, uint32_t seed)
{
	if (pixelType != PixelType::GrayFloat32&&pixelType != PixelType::ComplexFloat32)
	{
		throw std::runtime_error("Not supported.");
	}

	auto bitmap = CBitmapData<CHeapAllocator>::Create(pixelType, width, height);
	auto lockInfo = bitmap->Lock();
	int mul = (pixelType == PixelType::ComplexFloat32) ? 2 : 1;
	IppiSize size = { width*mul, height };
	IppStatus status = ippiSet_32f_C1R(0.0f, (float*)lockInfo.ptrDataRoi, lockInfo.pitch, size);
	status = ippiAddRandUniform_Direct_32f_C1IR((float*)lockInfo.ptrDataRoi, lockInfo.pitch, size, low, high, &seed);
	bitmap->Unlock();
	return bitmap;
}